use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::rc::Rc;
use crate::scanner::LiteralValue::{Empty, Nil};
use crate::util;


lazy_static! {
    static ref KEYWORD_MAP: HashMap<String, TokenType> = {
        let mut m = HashMap::new();
        m.insert("and".to_string(), TokenType::And);
        m.insert("class".to_string(), TokenType::Class);
        m.insert("else".to_string(), TokenType::Else);
        m.insert("false".to_string(), TokenType::False);
        m.insert("for".to_string(), TokenType::For);
        m.insert("fun".to_string(), TokenType::Fun);
        m.insert("if".to_string(), TokenType::If);
        m.insert("nil".to_string(), TokenType::Nil);
        m.insert("or".to_string(), TokenType::Or);
        m.insert("print".to_string(), TokenType::Print);
        m.insert("return".to_string(), TokenType::Return);
        m.insert("super".to_string(),  TokenType::Super);
        m.insert("this".to_string(), TokenType::This);
        m.insert("true".to_string(), TokenType::True);
        m.insert("var".to_string(), TokenType::Var);
        m.insert("while".to_string(), TokenType::While);
        m.insert("break".to_string(), TokenType::Break);
        m.insert("continue".to_string(), TokenType::Continue);
        m
    };
}


#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum TokenType {
    LeftParent, RightParent, LeftBrace, RightBrace,
    Comma, Dot, Minus, Plus, Semicolon, Slash, Star,

    // 感叹号
    Bang, BangEqual,
    Equal, EqualEqual,
    Greater, GreaterEqual,
    Less, LessEqual,

    Identifier, String, Number,

    And, Class, Else, False, Fun, For, If, Nil, Or,
    Print, Return, Super, This, True, Var, While,
    QuestionMark, Colon, Break, Continue,

    Eof
}

#[derive(Debug, Clone, PartialEq)]
pub enum LiteralValue {
    String(String),
    Number(f64),
    Bool(bool),
    Nil,
    Empty
}

impl Hash for LiteralValue {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match self {
            LiteralValue::String(s) => s.hash(state),
            LiteralValue::Number(f) => f.to_bits().hash(state),
            LiteralValue::Bool(b) => b.hash(state),
            LiteralValue::Nil => Nil.hash(state),
            LiteralValue::Empty => {}
        }
    }
}

impl Eq for LiteralValue {

}

impl LiteralValue {
    pub fn stringify(&self) -> String {
        match self {
            LiteralValue::String(s) => format!("{}", s),
            LiteralValue::Number(f) => {
                let ret = format!("{}", f);
                if ret.ends_with(".0") {
                    ret.strip_suffix(".0").unwrap().to_string()
                } else {
                    ret
                }
            }

            LiteralValue::Bool(b) => format!("{}", b),
            LiteralValue::Nil => "nil".to_string(),
            LiteralValue::Empty => "Empty".to_string()
        }
    }
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Token {
    pub t_type: TokenType,
    pub lexeme: String,
    pub literal: LiteralValue,
    pub line: usize
}

type ScanResult<E> = Result<(), E>;

#[derive(Debug)]
pub struct Scanner {
    source: String,
    tokens: Vec<Token>,
    start: usize,
    current: usize,
    line: usize,
    last_result: ScanResult<String>
}

impl Scanner {
    pub fn new(line: &str) -> Self {
        Self {source: line.to_string(), tokens: vec![], start: 0, current: 0, line: 1, last_result: Ok(())}
    }

    pub fn scan_tokens(&mut self) -> Result<Vec<Token>, String>{
        while !self.is_at_end() {
            self.start = self.current;
            self.scan_token()?;
        }

        self.start = self.current;
        self.add_token(TokenType::Eof, LiteralValue::Empty);
        Ok(self.tokens.clone())
    }

    pub fn scan_token(&mut self) -> Result<(), String> {
        let c = self.advance();
        match c {
            '?' => self.add_token(TokenType::QuestionMark, LiteralValue::Empty),
            ':' => self.add_token(TokenType::Colon, LiteralValue::Empty),
            '(' => self.add_token(TokenType::LeftParent, LiteralValue::Empty),
            ')' => self.add_token(TokenType::RightParent, LiteralValue::Empty),
            '{' => self.add_token(TokenType::LeftBrace, LiteralValue::Empty),
            '}' => self.add_token(TokenType::RightBrace, LiteralValue::Empty),
            ',' => self.add_token(TokenType::Comma, LiteralValue::Empty),
            '.' => self.add_token(TokenType::Dot, LiteralValue::Empty),
            '-' => self.add_token(TokenType::Minus, LiteralValue::Empty),
            '+' => self.add_token(TokenType::Plus, LiteralValue::Empty),
            ';' => self.add_token(TokenType::Semicolon, LiteralValue::Empty),
            '*' => self.add_token(TokenType::Star, LiteralValue::Empty),

            '!' => {
                let token: TokenType = if self.match_char('=') { TokenType::BangEqual } else { TokenType::Bang };
                self.add_token(token, LiteralValue::Empty);
             }
            '=' => {
                let token = if self.match_char('=') { TokenType::EqualEqual } else { TokenType::Equal };
                self.add_token(token, LiteralValue::Empty);
            }
            '<' => {
                let token = if self.match_char('=') { TokenType::LessEqual } else { TokenType::Less };
                self.add_token(token, LiteralValue::Empty);
            }
            '>' => {
                let token = if self.match_char('=') { TokenType::GreaterEqual } else { TokenType::Greater };
                self.add_token(token, LiteralValue::Empty);
            } 

            '/' => {
                if self.match_char('/') {
                    while self.peek() != '\n' && !self.is_at_end() {
                        self.advance();
                    }
                } else {
                    self.add_token(TokenType::Slash, LiteralValue::Empty);
                }
            }

            ' ' => {}
            '\r' => {}
            '\t' => {}
            '\n' => self.line += 1,

            '"' => self.string(),

            _ => {
                if c.is_ascii_digit() {
                    self.number(c);
                } else if c.is_ascii_alphabetic() {
                    self.identifier(c);
                } else {
                    self.set_err("Unexpected character.");
                }
            }
        }
        self.last_result.clone()
    }

    fn is_at_end(&self) -> bool {
        self.current >= self.source.len()
    }

    fn advance(&mut self) -> char {
        let ret = self.source.chars().nth(self.current).unwrap();
        self.current += 1;
        return ret;
    }

    fn add_token(&mut self, token: TokenType, value: LiteralValue) {
        let token_ex = Token { t_type: token, lexeme: self.source[self.start..self.current].to_string(), line: self.line, literal: value };
        self.tokens.push(token_ex);
        self.clear_err();
    }

    // 如果匹配，则指针移到下一个字符；否则不动
    fn match_char(&mut self, c: char) -> bool {
        if self.is_at_end() {
            false
        } else {
            if self.source.chars().nth(self.current).unwrap() == c {
                self.current += 1;
                true
            } else {
                false
            }
        }
    }

    fn string(&mut self) {
        let mut s = String::new();

        while self.peek() != '"' && !self.is_at_end() {
            if self.peek() == '\n' { self.line += 1; }
            s.push(self.advance());
        }

        if self.is_at_end() {
            self.set_err("Unterminated string.");
            return;
        }

        self.advance();

        self.add_token(TokenType::String, LiteralValue::String(s));
    }

    fn clear_err(&mut self) {
        self.last_result = Ok(());
    }

    fn set_err(&mut self, err: &str) {
        self.last_result = Err(util::to_err_msg(self.line, err));
    }
    fn number(&mut self, first_char: char) {
        let mut number_str = String::new();
        number_str.push(first_char);
        while self.peek().is_ascii_digit() {
            number_str.push(self.advance());
        }

        if self.peek() == '.' && self.peek_next().is_ascii_digit() {
            self.advance();

            while self.peek().is_ascii_digit() {
                number_str.push(self.advance());
            }
        }

        self.add_token(TokenType::Number, LiteralValue::Number(number_str.parse::<f64>().unwrap()));
    }
    fn identifier(&mut self, first_char: char) {
        let mut s = String::new();
        s.push(first_char);
        while self.peek().is_ascii_alphanumeric() {
            s.push(self.advance());
        }

        match KEYWORD_MAP.get(&s) {
            Some(v) => self.add_token(v.clone(), LiteralValue::Empty),
            None => self.add_token(TokenType::Identifier, LiteralValue::String(s))
        }
    }

    fn peek(&self) -> char {
        if self.is_at_end() { '\0' }
        else { self.source.chars().nth(self.current).unwrap() }
    }

    fn peek_next(&self) -> char {
        if self.is_at_end() { '\0' }
        else { self.source.chars().nth(self.current + 1).unwrap() }
    }
}
